#include "hdddata.h"
#include "database.h"
#include <sstream>

//for debugging
#include <iostream> 

#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fstream>


using namespace std;

namespace client_server{
	HDDdata::HDDdata(){
		isFile = 0x8;
		isFolder = 0x4;
		dirp = opendir("database");
		if(dirp == 0){
			mkdir("database", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
		} else {
			ifstream in;
			in.open("database/init");
			unsigned int ngCounter;
			in >> ngCounter;
			setNgCounter(ngCounter);
			in.close();
		}
	}

	HDDdata::~HDDdata(){}	
	
	string parseFileName(const string& fname, unsigned int fifo, unsigned int mode ){ 
		stringstream s(fname);
		string buf;
		s>> buf;
		if(buf != "NG"){
			return "unknown";
		}
		s>>buf;
		if(mode==HDDdata::ID){
			return buf;
		}
		s>>buf;
		unsigned int l=atoi(buf.c_str());
		string name;
			s >> name;
			while(name.size() < l){
				string temp;
				s >> temp;
				name += " " + temp;
			}	
		if(mode==HDDdata::NAME){
			return name;
		}
		if((fifo==HDDdata::FILE)&&(mode==HDDdata::AUTHOR)){
			s>>buf;
			l=atoi(buf.c_str());
			string author;
			s >> author;
			while(author.size() < l){
				string temp;
				s >> temp;
				author += " " + temp;
			}
			return author;
		}
		return "unknown";
	}

	vector<pair<int, string>> HDDdata::listAllNewsGroups(){
		dirp = opendir("database");
		vector<pair<int, string>> ans;
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				if(dp->d_type == isFolder){
					int ngId = atoi(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::ID).c_str());
					string ngName = parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::NAME);
					if(ngName != "unknown"){
						ans.push_back(make_pair(ngId, ngName));
					}
				}
			}
			dp = readdir(dirp);
		}
		return ans;
	}

	void HDDdata::createNewsGroup(const string& name) throw (NewsGroupDoesNotExistException){
		dirp = opendir("database");
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				if(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::NAME) == name && dp->d_type == isFolder){
					throw NewsGroupDoesNotExistException();
				}
			}
			dp = readdir(dirp);
		}

		ngIncrement();
		int n = ngNum();

		string s = "database/NG ";
		stringstream ss;
		ss << n;
		s.append(ss.str());

		s.append(" ");
		stringstream ss2;
		ss2 << name.length();
		s.append(ss2.str());

		s.append(" ");
		s.append(name);

		mkdir(s.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

		ofstream out;
		out.open("database/init", ofstream::trunc);
		out << n;
		out.close();
	}

	void HDDdata::deleteNewsGroup(unsigned int id) throw (NewsGroupDoesNotExistException){
		dirp = opendir("database");
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				unsigned int ngId = atoi(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::ID).c_str());
				if(ngId == id && dp->d_type == isFolder){
					string path = "database/";				
					string name = dp->d_name;
					path = path + name;

					DIR* folder;
					folder = opendir(path.c_str());
					dirent* fp = readdir(folder);
					while(fp != 0){
						if(string (".").compare(fp->d_name) != false && string ("..").compare(fp->d_name) != false){
							string fpath = path;
							fpath += "/";
							string fname;
							fname = fp->d_name;
							fpath += fname;
							remove(fpath.c_str());
							closedir(folder);
						}
						fp = readdir(folder);
					}
					remove(path.c_str());
					return;
				}
			}
			dp = readdir(dirp);
		}
		throw NewsGroupDoesNotExistException();
	}
	
	vector<pair<int,string> > HDDdata::listAllArticles(unsigned int id) throw (NewsGroupDoesNotExistException){
		dirp = opendir("database");
		vector<pair<int, string>> result;
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				unsigned int ngId = atoi(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::ID).c_str());
				if(ngId == id && dp->d_type == isFolder){
					DIR* folder;
					string path = "database/";				
					string name = dp->d_name;
					path = path + name;
					folder = opendir(path.c_str());
					dirent* fp = readdir(folder);
					while(fp != 0){
						if(fp->d_type == isFile){
							int artId = atoi(parseFileName(fp->d_name, HDDdata::FILE, HDDdata::ID).c_str());
							string artName = parseFileName(fp->d_name, HDDdata::FILE, HDDdata::NAME);
							result.push_back(make_pair(artId, artName));
						}
						fp = readdir(folder);
					}
					closedir(folder);
					return result;
				}
			}
			dp = readdir(dirp);
		}
		throw NewsGroupDoesNotExistException();
	}

	void HDDdata::readArticle(unsigned int ngId, unsigned int aId, stringstream& ss) throw (NewsGroupDoesNotExistException, ArticleDoesNotExistException){
		dirp = opendir("database");
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				unsigned int ngid = atoi(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::ID).c_str());
				if(ngid == ngId && dp->d_type == isFolder){
					DIR* folder;
					string path = "database/";				
					string name = dp->d_name;
					path = path + name;
					folder = opendir(path.c_str());
					dirent* fp = readdir(folder);
					while(fp != 0){
						if(string (".").compare(fp->d_name) != false && string ("..").compare(fp->d_name) != false){
							unsigned int artId = atoi(parseFileName(fp->d_name, HDDdata::FILE, HDDdata::ID).c_str());
							if(artId == aId && fp->d_type == isFile){
								string artName = parseFileName(fp->d_name, HDDdata::FILE, HDDdata::NAME);
								string artAuthor = parseFileName(fp->d_name, HDDdata::FILE, HDDdata::AUTHOR);
						
								string path = "database/";				
								string name = dp->d_name;
								path = path + name;
								name = "/";
								path = path + name;
								name = fp->d_name;
								path = path + name;
								ifstream article (path);
								string artContent;

								string temp;
								if (article.is_open()){
									if(article.good()){
										getline (article,temp);
										artContent += temp;
									}
									while (article.good()){
										getline (article,temp);
										artContent += "\n" + temp;
									}
									article.close();
								}

								ss << artName.size() << " " << artName;
								ss << " " << artAuthor.size() << " " << artAuthor;
								ss << " " << artContent.size() << " " << artContent;
								closedir(folder);
								return;
							}
						}
						fp = readdir(folder);
					}
					closedir(folder);
					throw ArticleDoesNotExistException();
				}
			}
			dp = readdir(dirp);
		}
		throw NewsGroupDoesNotExistException();
	}

	void HDDdata::createArticle(unsigned int id,const string& artName, const string& author, const string& content) throw (NewsGroupDoesNotExistException){
		dirp = opendir("database");
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				unsigned int ngid = atoi(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::ID).c_str());
				if(ngid == id && dp->d_type == isFolder){
					stringstream path;
					path << "database";
					path << "/";
					string name = dp->d_name;
					path << name;
					path << "/NG ";

					increment();
					unsigned int n = articleNum();
					path << n;
					path << " ";
					path << artName.size();
					path << " ";
					path << artName;
					path << " ";
					path << author.size();
					path << " ";
					path << author;

					ofstream oFile;
					oFile.open(path.str());
					if(oFile.is_open()){
						oFile << content << endl;
					}
					oFile.close();
					return;
				}
			}
			dp = readdir(dirp);
		}
		throw NewsGroupDoesNotExistException();
	}

	void HDDdata::deleteArticle(unsigned int ngId, unsigned int aId) throw (NewsGroupDoesNotExistException, ArticleDoesNotExistException){
		dirp = opendir("database");
		dirent* dp = readdir(dirp);
		while(dp != 0){
			if(string (".").compare(dp->d_name) != false && string ("..").compare(dp->d_name) != false){
				unsigned int ngid = atoi(parseFileName(dp->d_name, HDDdata::FOLDER, HDDdata::ID).c_str());
				if(ngid == ngId && dp->d_type == isFolder){
					DIR* folder;
					string path = "database/";				
					string name = dp->d_name;
					path = path + name;
					folder = opendir(path.c_str());
					dirent* fp = readdir(folder);
					while(fp != 0){
						if(string (".").compare(fp->d_name) != false && string ("..").compare(fp->d_name) != false){
							unsigned int artId = atoi(parseFileName(fp->d_name, HDDdata::FILE, HDDdata::ID).c_str());
							if(artId == aId && fp->d_type == isFile){
								string path = "database/";				
								string name = dp->d_name;
								path = path + name;
								path += "/";
								name = fp->d_name;
								path += name;
								remove(path.c_str());
								closedir(folder);
								return;
							}
						}
						fp = readdir(folder);
					}
					closedir(folder);
					throw ArticleDoesNotExistException();
				}
			}
			dp = readdir(dirp);
		}
		throw NewsGroupDoesNotExistException();
	}		
}
